/* ------------------------- htmlparser/element.js -------------------------- */
VED.htmlParser.element = new Class({
   initialize: function( name, attributes ) {
      this.name = name;
      this.attributes = attributes || {};
      this.children = [];

      // Reveal the real semantic of our internal custom tag name (#6639),
      // when resolving whether it's block like.
      var realName = name || '',
         prefixed = realName.match( /^ved:(.*)/ );
      prefixed && ( realName = prefixed[ 1 ] );

      var isBlockLike = !!( VED.dtd.$nonBodyContent[ realName ] || VED.dtd.$block[ realName ] || VED.dtd.$listItem[ realName ] || VED.dtd.$tableContent[ realName ] || VED.dtd.$nonEditable[ realName ] || realName == 'br' );

      this.isEmpty = !!VED.dtd.$empty[ name ];
      this.isUnknown = !VED.dtd[ name ];

      this._ = {
         isBlockLike: isBlockLike,
         hasInlineStarted: this.isEmpty || !isBlockLike
      };
   }
});

VED.htmlParser.cssStyle = function() {
   var styleText,
      arg = arguments[ 0 ],
      rules = {};

   styleText = arg instanceof VED.htmlParser.element ? arg.attributes.style : arg;

   // html-encoded quote might be introduced by 'font-family'
   // from MS-Word which confused the following regexp. e.g.
   //'font-family: &quot;Lucida, Console&quot;'
   // TODO reuse CSS methods from tools.
   ( styleText || '' ).replace( /&quot;/g, '"' ).replace( /\s*([^ :;]+)\s*:\s*([^;]+)\s*(?=;|$)/g, function( match, name, value ) {
      name == 'font-family' && ( value = value.replace( /["']/g, '' ) );
      rules[ name.toLowerCase() ] = value;
   });

   return {

      rules: rules,

      populate: function( obj ) {
         var style = this.toString();
         if( style ) {
            obj instanceof Element ? obj.setAttribute( 'style', style ) : obj instanceof VED.htmlParser.element ? obj.attributes.style = style : obj.style = style;
         }
      },

      toString: function() {
         var output = [];
         for ( var i in rules )
            rules[ i ] && output.push( i, ':', rules[ i ], ';' );
         return output.join( '' );
      }
   };
};

(function() {
   // Used to sort attribute entries in an array, where the first element of
   // each object is the attribute name.
   var sortAttribs = function( a, b ) {
         a = a[ 0 ];
         b = b[ 0 ];
         return a < b ? -1 : a > b ? 1 : 0;
      };

   VED.htmlParser.element.implement({

      type: 1,

      add: VED.htmlParser.fragment.prototype.add,

      clone: function() {
         return new VED.htmlParser.element( this.name, this.attributes );
      },

      writeHtml: function( writer, filter ) {
         var attributes = this.attributes;

         // Ignore ved: prefixes when writing HTML.
         var element = this,
            writeName = element.name,
            a, newAttrName, value;

         var isChildrenFiltered;

         element.filterChildren = function() {
            if( !isChildrenFiltered ) {
               var writer = new VED.htmlParser.basicWriter();
               VED.htmlParser.fragment.prototype.writeChildrenHtml.call( element, writer, filter );
               element.children = new VED.htmlParser.fragment.fromHtml( writer.getHtml(), element.clone(), 0 ).children;
               isChildrenFiltered = 1;
            }
         };

         if( filter ) {

            // Filtering if it's the root node.
            if( !this.parent ) filter.onRoot( this );

            while( true ) {
               if( !( writeName = filter.onElementName( writeName ) ) ) return;

               element.name = writeName;

               if( !( element = filter.onElement( element ) ) ) return;

               element.parent = this.parent;

               if( element.name == writeName ) break;

               // If the element has been replaced with something of a
               // different type, then make the replacement write itself.
               if( element.type != 1 ) {
                  element.writeHtml( writer, filter );
                  return;
               }

               writeName = element.name;

               // This indicate that the element has been dropped by
               // filter but not the children.
               if( !writeName ) {
                  // Fix broken parent refs.
                  for ( var c = 0, length = this.children.length; c < length; c++ )
                     this.children[ c ].parent = element.parent;

                  this.writeChildrenHtml.call( element, writer, isChildrenFiltered ? null : filter );
                  return;
               }
            }

            // The element may have been changed, so update the local
            // references.
            attributes = element.attributes;
         }

         // Open element tag.
         writer.openTag( writeName, attributes );

         // Copy all attributes to an array.
         var attribsArray = [];
         // Iterate over the attributes twice since filters may alter
         // other attributes.
         for ( var i = 0; i < 2; i++ ) {
            for ( a in attributes ) {
               newAttrName = a;
               value = attributes[ a ];
               if( i == 1 )
                  attribsArray.push( [ a, value ] );
               else if( filter ) {
                  while ( true ) {
                     if( !( newAttrName = filter.onAttributeName( a ) ) ) {
                        delete attributes[ a ];
                        break;
                     } else if( newAttrName != a ) {
                        delete attributes[ a ];
                        a = newAttrName;
                        continue;
                     } else
                        break;
                  }
                  if( newAttrName ) {
                     if( ( value = filter.onAttribute( element, newAttrName, value ) ) === false )
                        delete attributes[ newAttrName ];
                     else
                        attributes[ newAttrName ] = value;
                  }
               }
            }
         }
         // Sort the attributes by name.
         if( writer.sortAttributes )
            attribsArray.sort( sortAttribs );

         // Send the attributes.
         var len = attribsArray.length;
         for ( i = 0; i < len; i++ ) {
            var attrib = attribsArray[ i ];
            writer.attribute( attrib[ 0 ], attrib[ 1 ] );
         }

         // Close the tag.
         writer.openTagClose( writeName, element.isEmpty );

         if( !element.isEmpty ) {
            this.writeChildrenHtml.call( element, writer, isChildrenFiltered ? null : filter );
            // Close the element.
            writer.closeTag( writeName );
         }
      },

      writeChildrenHtml: function( writer, filter ) {
         // Send children.
         VED.htmlParser.fragment.prototype.writeChildrenHtml.apply( this, arguments );
      }
   });
})();




